﻿using System;
using NUnit.Framework;

namespace POCOFirst
{
	[TestFixture]
	public class TestDriven
	{
		private OrderRepository _orderRepository;
		private CustomerRepository _customerRepository;

		[Test]
		public void CanCreateOrder()
		{
			var o = OrderFactory.CreateOrder(new Customer());
			Assert.IsNotNull(o);
		}

		[Test]
		public void CanCreateOrderWithCustomer()
		{
			var o = OrderFactory.CreateOrder(new Customer());
			Assert.IsNotNull(o.Customer);
		}

		[Test]
		public void OrderDateIsCurrentAfterCreation()
		{
			var beforeCreation = DateTime.Now.AddMilliseconds(-1);
			var o = OrderFactory.CreateOrder(new Customer());
			var afterCreation = DateTime.Now.AddMilliseconds(1);

			Assert.IsTrue(o.OrderDate > beforeCreation);
			Assert.IsTrue(o.OrderDate < afterCreation);
		}

		[Test]
		public void OrderNumberIsZeroAfterCreation()
		{
			var o = OrderFactory.CreateOrder(new Customer());
			Assert.AreEqual(0, o.OrderNumber);
		}

		[SetUp]
		public void Setup()
		{
			_orderRepository = new OrderRepository();
			_customerRepository = new CustomerRepository();
		}

		[Test]
		public void CanGetOrderFromRepository()
		{
			const int number = 5;
			_orderRepository.AddEntity(FakeOrder(number));

			var o = this._orderRepository.GetEntityByIdentity(number);
			Assert.IsNotNull(o);
			Assert.AreEqual(number, o.OrderNumber);
		}

		[Test]
		public void CanAddOrder()
		{
			var o = FakeOrder(15);
			// it can't access the count of orders in repository directly,
			// so it can't assert something without getting changes of repository.
			_orderRepository.AddEntity(o);
		}

		[Test]
		public void CanAddCustomer()
		{
			var c = new Customer();
			_customerRepository.AddEntity(c);
		}

		private Order FakeOrder(int ordernumber)
		{
			var o = OrderFactory.CreateOrder(new Customer());
			SetPropertyValue(o, "OrderNumber", ordernumber);
			return o;
		}

		[Test]
		public void CanSetPropertyValue()
		{
			const int ordernumber = 42;
			var o = OrderFactory.CreateOrder(new Customer());
			SetPropertyValue(o, "OrderNumber", ordernumber);

			Assert.AreEqual(ordernumber, o.OrderNumber);
		}

		public static void SetPropertyValue(object instance, string propertyname, object newvalue)
		{
			var typeInstance = instance.GetType();
			var property = typeInstance.GetProperty(propertyname);

			var typeValue = newvalue.GetType();
			if (property.PropertyType.IsEquivalentTo(typeValue))
				property.SetValue(instance, newvalue, null);
			else
				throw new ArgumentException("type mismatch.");
		}

		public void FakeOrderWithNumberAndCustomer(int ordernumber, Customer customer, OrderRepository repository)
		{
			var o = OrderFactory.CreateOrder(customer);
			SetPropertyValue(o, "OrderNumber", ordernumber);
			repository.AddEntity(o);

			Assert.IsNotNull(o);
			Assert.IsNotNull(o.Customer);
			Assert.AreEqual(o.OrderNumber, repository.GetEntityByIdentity(ordernumber).OrderNumber);
		}

		public Customer FakeCustomer(int customernumber)
		{
			var customer = new Customer();
			SetPropertyValue(customer, "CustomerNumber", customernumber);
			return customer;
		}

		[Test]
		public void CanGetOrdersByCustomer()
		{
			var customer = FakeCustomer(7);

			FakeOrderWithNumberAndCustomer(42, customer, _orderRepository);
			FakeOrderWithNumberAndCustomer(12, customer, _orderRepository);
			FakeOrderWithNumberAndCustomer(3, customer, _orderRepository);

			FakeOrderWithNumberAndCustomer(3, FakeCustomer(2), _orderRepository);
			FakeOrderWithNumberAndCustomer(1, FakeCustomer(33), _orderRepository);

			Assert.AreEqual(3, _orderRepository.GetListByParent(customer).Count);
		}
	}
}